/**
 * Copyright (c) 2006-2015, JGraph Ltd
 * Copyright (c) 2006-2015, Gaudenz Alder
 */
function mxEdgeSegmentHandler (state) {
  mxEdgeHandler.call(this, state)
};

/**
 * Extends mxEdgeHandler.
 */
mxUtils.extend(mxEdgeSegmentHandler, mxElbowEdgeHandler)

/**
 * Function: getCurrentPoints
 *
 * Returns the current absolute points.
 */
mxEdgeSegmentHandler.prototype.getCurrentPoints = function () {
  var pts = this.state.absolutePoints

  if (pts != null) {
    // Special case for straight edges where we add a virtual middle handle for moving the edge
    var tol = Math.max(1, this.graph.view.scale)

    if (pts.length == 2 || (pts.length == 3 &&
			(Math.abs(pts[0].x - pts[1].x) < tol && Math.abs(pts[1].x - pts[2].x) < tol ||
			Math.abs(pts[0].y - pts[1].y) < tol && Math.abs(pts[1].y - pts[2].y) < tol))) {
      var cx = pts[0].x + (pts[pts.length - 1].x - pts[0].x) / 2
      var cy = pts[0].y + (pts[pts.length - 1].y - pts[0].y) / 2

      pts = [pts[0], new mxPoint(cx, cy), new mxPoint(cx, cy), pts[pts.length - 1]]
    }
  }

  return pts
}

/**
 * Function: getPreviewPoints
 *
 * Updates the given preview state taking into account the state of the constraint handler.
 */
mxEdgeSegmentHandler.prototype.getPreviewPoints = function (point) {
  if (this.isSource || this.isTarget) {
    return mxElbowEdgeHandler.prototype.getPreviewPoints.apply(this, arguments)
  } else {
    var pts = this.getCurrentPoints()
    var last = this.convertPoint(pts[0].clone(), false)
    point = this.convertPoint(point.clone(), false)
    var result = []

    for (var i = 1; i < pts.length; i++) {
      var pt = this.convertPoint(pts[i].clone(), false)

      if (i == this.index) {
        if (Math.round(last.x - pt.x) == 0) {
          last.x = point.x
          pt.x = point.x
		 		}

        if (Math.round(last.y - pt.y) == 0) {
		 			last.y = point.y
		 			pt.y = point.y
		 		}
      }

      if (i < pts.length - 1) {
        result.push(pt)
      }

      last = pt
    }

    // Replaces single point that intersects with source or target
    if (result.length == 1) {
      var source = this.state.getVisibleTerminalState(true)
      var target = this.state.getVisibleTerminalState(false)
      var scale = this.state.view.getScale()
      var tr = this.state.view.getTranslate()

      var x = result[0].x * scale + tr.x
      var y = result[0].y * scale + tr.y

      if ((source != null && mxUtils.contains(source, x, y)) ||
				(target != null && mxUtils.contains(target, x, y))) {
        result = [point, point]
      }
    }

    return result
  }
}

/**
 * Function: updatePreviewState
 *
 * Overridden to perform optimization of the edge style result.
 */
mxEdgeSegmentHandler.prototype.updatePreviewState = function (edge, point, terminalState, me) {
  mxEdgeHandler.prototype.updatePreviewState.apply(this, arguments)

  // Checks and corrects preview by running edge style again
  if (!this.isSource && !this.isTarget) {
    point = this.convertPoint(point.clone(), false)
    var pts = edge.absolutePoints
    var pt0 = pts[0]
    var pt1 = pts[1]

    var result = []

    for (var i = 2; i < pts.length; i++) {
      var pt2 = pts[i]

      // Merges adjacent segments only if more than 2 to allow for straight edges
      if ((Math.round(pt0.x - pt1.x) != 0 || Math.round(pt1.x - pt2.x) != 0) &&
				(Math.round(pt0.y - pt1.y) != 0 || Math.round(pt1.y - pt2.y) != 0)) {
        result.push(this.convertPoint(pt1.clone(), false))
      }

      pt0 = pt1
      pt1 = pt2
    }

    var source = this.state.getVisibleTerminalState(true)
    var target = this.state.getVisibleTerminalState(false)
    var rpts = this.state.absolutePoints

    // A straight line is represented by 3 handles
    if (result.length == 0 && (Math.round(pts[0].x - pts[pts.length - 1].x) == 0 ||
			Math.round(pts[0].y - pts[pts.length - 1].y) == 0)) {
      result = [point, point]
    }
    // Handles special case of transitions from straight vertical to routed
    else if (pts.length == 5 && result.length == 2 && source != null && target != null &&
				rpts != null && Math.round(rpts[0].x - rpts[rpts.length - 1].x) == 0) {
      var view = this.graph.getView()
      var scale = view.getScale()
      var tr = view.getTranslate()

      var y0 = view.getRoutingCenterY(source) / scale - tr.y

      // Use fixed connection point y-coordinate if one exists
      var sc = this.graph.getConnectionConstraint(edge, source, true)

      if (sc != null) {
        var pt = this.graph.getConnectionPoint(source, sc)

        if (pt != null) {
          this.convertPoint(pt, false)
          y0 = pt.y
        }
      }

      var ye = view.getRoutingCenterY(target) / scale - tr.y

      // Use fixed connection point y-coordinate if one exists
      var tc = this.graph.getConnectionConstraint(edge, target, false)

      if (tc) {
        var pt = this.graph.getConnectionPoint(target, tc)

        if (pt != null) {
          this.convertPoint(pt, false)
          ye = pt.y
        }
      }

      result = [new mxPoint(point.x, y0), new mxPoint(point.x, ye)]
    }

    this.points = result

    // LATER: Check if points and result are different
    edge.view.updateFixedTerminalPoints(edge, source, target)
    edge.view.updatePoints(edge, this.points, source, target)
    edge.view.updateFloatingTerminalPoints(edge, source, target)
  }
}

/**
 * Overriden to merge edge segments.
 */
mxEdgeSegmentHandler.prototype.connect = function (edge, terminal, isSource, isClone, me) {
  var model = this.graph.getModel()
  var geo = model.getGeometry(edge)
  var result = null

  // Merges adjacent edge segments
  if (geo != null && geo.points != null && geo.points.length > 0) {
    var pts = this.abspoints
    var pt0 = pts[0]
    var pt1 = pts[1]
    result = []

    for (var i = 2; i < pts.length; i++) {
      var pt2 = pts[i]

      // Merges adjacent segments only if more than 2 to allow for straight edges
      if ((Math.round(pt0.x - pt1.x) != 0 || Math.round(pt1.x - pt2.x) != 0) &&
				(Math.round(pt0.y - pt1.y) != 0 || Math.round(pt1.y - pt2.y) != 0)) {
        result.push(this.convertPoint(pt1.clone(), false))
      }

      pt0 = pt1
      pt1 = pt2
    }
  }

  model.beginUpdate()
  try {
    if (result != null) {
      var geo = model.getGeometry(edge)

      if (geo != null) {
        geo = geo.clone()
        geo.points = result

        model.setGeometry(edge, geo)
      }
    }

    edge = mxEdgeHandler.prototype.connect.apply(this, arguments)
  } finally {
    model.endUpdate()
  }

  return edge
}

/**
 * Function: getTooltipForNode
 *
 * Returns no tooltips.
 */
mxEdgeSegmentHandler.prototype.getTooltipForNode = function (node) {
  return null
}

/**
 * Function: createBends
 *
 * Adds custom bends for the center of each segment.
 */
mxEdgeSegmentHandler.prototype.start = function (x, y, index) {
  mxEdgeHandler.prototype.start.apply(this, arguments)

  if (this.bends != null && this.bends[index] != null &&
		!this.isSource && !this.isTarget) {
    mxUtils.setOpacity(this.bends[index].node, 100)
  }
}

/**
 * Function: createBends
 *
 * Adds custom bends for the center of each segment.
 */
mxEdgeSegmentHandler.prototype.createBends = function () {
  var bends = []

  // Source
  var bend = this.createHandleShape(0)
  this.initBend(bend)
  bend.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE)
  bends.push(bend)

  var pts = this.getCurrentPoints()

  // Waypoints (segment handles)
  if (this.graph.isCellBendable(this.state.cell)) {
    if (this.points == null) {
      this.points = []
    }

    for (var i = 0; i < pts.length - 1; i++) {
      bend = this.createVirtualBend()
      bends.push(bend)
      var horizontal = Math.round(pts[i].x - pts[i + 1].x) == 0

      // Special case where dy is 0 as well
      if (Math.round(pts[i].y - pts[i + 1].y) == 0 && i < pts.length - 2) {
        horizontal = Math.round(pts[i].x - pts[i + 2].x) == 0
      }

      bend.setCursor((horizontal) ? 'col-resize' : 'row-resize')
      this.points.push(new mxPoint(0, 0))
    }
  }

  // Target
  var bend = this.createHandleShape(pts.length)
  this.initBend(bend)
  bend.setCursor(mxConstants.CURSOR_TERMINAL_HANDLE)
  bends.push(bend)

  return bends
}

/**
 * Function: redraw
 *
 * Overridden to invoke <refresh> before the redraw.
 */
mxEdgeSegmentHandler.prototype.redraw = function () {
  this.refresh()
  mxEdgeHandler.prototype.redraw.apply(this, arguments)
}

/**
 * Function: redrawInnerBends
 *
 * Updates the position of the custom bends.
 */
mxEdgeSegmentHandler.prototype.redrawInnerBends = function (p0, pe) {
  if (this.graph.isCellBendable(this.state.cell)) {
    var pts = this.getCurrentPoints()

    if (pts != null && pts.length > 1) {
      var straight = false

      // Puts handle in the center of straight edges
      if (pts.length == 4 && Math.round(pts[1].x - pts[2].x) == 0 && Math.round(pts[1].y - pts[2].y) == 0) {
        straight = true

        if (Math.round(pts[0].y - pts[pts.length - 1].y) == 0) {
          var cx = pts[0].x + (pts[pts.length - 1].x - pts[0].x) / 2
          pts[1] = new mxPoint(cx, pts[1].y)
          pts[2] = new mxPoint(cx, pts[2].y)
        } else {
          var cy = pts[0].y + (pts[pts.length - 1].y - pts[0].y) / 2
          pts[1] = new mxPoint(pts[1].x, cy)
          pts[2] = new mxPoint(pts[2].x, cy)
        }
      }

      for (var i = 0; i < pts.length - 1; i++) {
        if (this.bends[i + 1] != null) {
		 			var p0 = pts[i]
	 				var pe = pts[i + 1]
			 		var pt = new mxPoint(p0.x + (pe.x - p0.x) / 2, p0.y + (pe.y - p0.y) / 2)
			 		var b = this.bends[i + 1].bounds
			 		this.bends[i + 1].bounds = new mxRectangle(Math.floor(pt.x - b.width / 2),
			 				Math.floor(pt.y - b.height / 2), b.width, b.height)
				 	this.bends[i + 1].redraw()

				 	if (this.manageLabelHandle) {
            this.checkLabelHandle(this.bends[i + 1].bounds)
          }
        }
      }

      if (straight) {
        mxUtils.setOpacity(this.bends[1].node, this.virtualBendOpacity)
        mxUtils.setOpacity(this.bends[3].node, this.virtualBendOpacity)
      }
    }
  }
}
